import asyncio
import re
import aiohttp
import os
from urllib import parse

from PyQt5.QtWidgets import QMessageBox


class YqDownload:
    def __init__(self, repo, yq_config, ui, mainWindow):
        self.repo = repo
        self.yq_config = yq_config
        self.ui = ui
        self.mainWindow = mainWindow
        self.save_done_num = 0
        self.save_error_num = 0
        self.save_error_doc = []
        self.num_to_save = 0

    async def download(self, checked_boxs):
        # 每次下载都重置下载信息数据
        self.save_done_num = 0
        self.save_error_num = 0
        self.save_error_doc = []
        self.num_to_save = len(checked_boxs)
        self.ui.progressBar.setRange(0, self.num_to_save)
        async with self.repo.YqRequest.semaphore:
            await asyncio.gather(
                *(self.do_save(key, doc_title) for key, doc_title in checked_boxs.items())
            )
            QMessageBox.information(self.mainWindow, "信息",
                                    f"下载完成，成功{self.save_done_num}篇，失败{self.save_error_num}篇。\n失败文章：{self.save_error_doc}",
                                    QMessageBox.Ok)

    # async def download_all(self, checked_boxs):
    #     await asyncio.gather(
    #         *(self.do_save(key, doc_title) for key, doc_title in checked_boxs.items())
    #     )
    #     QMessageBox.information(self.mainWindow, "信息", "下载完成！")

    # 获取文章并执行保存
    # async def do_save(self, repo_id, repo_name, doc_slug, doc_title):
    async def do_save(self, key, doc_title):
        try:
            key_split = key.split("#")
            if len(key_split) >= 3:
                repo_id = key_split[0]
                repo_name = key_split[1]
                doc_slug = key_split[2]

                url = f"/repos/{repo_id}/docs/{doc_slug}"

                result = await self.repo.YqRequest.do_async_get(url)
                md_body = result["data"]["body"]
                # 将文档中的外网图片地址替换为本地地址
                pattern = r"!\[(?P<pic_type>.*?)\]\((?P<pic_src>https:\/\/.*\/(?P<pic_name>.*?\.[a-zA-z]+)).*\)"
                repl = rf"![\g<pic_type>]({self.yq_config.pic_path}/{repo_name}/\g<pic_name>)"
                """
                groupdict()返回一个字典，包含所有已经命名的匹配子群，键：子群名，即上面的?P<...>中起的名字，值：匹配到的值
                """
                images = [image_info.groupdict() for image_info in re.finditer(pattern, md_body)]
                # 将图片地址转化为本地地址的md文档
                local_md_body = re.sub(pattern, repl, md_body)

                if images:
                    # 图片保存位置: .yuque/<repo_name>/assets/<filename>
                    pic_save_path = f"{self.yq_config.pic_path}/{repo_name}"
                    if not os.path.exists(pic_save_path):
                        os.makedirs(pic_save_path)
                    async with self.repo.YqRequest.semaphore:
                        async with aiohttp.ClientSession() as session:
                            await asyncio.gather(
                                *(self.save_image(session, image_info, pic_save_path) for image_info in images)
                            )

                self.save_doc(repo_name, doc_title, local_md_body)
        except:
            self.save_error_doc.append(doc_title)
            self.save_error_num += 1
        else:
            self.save_done_num += 1
            self.ui.progressBar.setValue(self.save_done_num)

    # 下载图片
    async def save_image(self, session, image_info: dict, save_dir: str):
        pic_src = image_info["pic_src"]
        pic_name = image_info["pic_name"]

        async with session.get(pic_src, timeout=self.repo.YqRequest.pic_timeout) as resp:
            with open(os.path.join(save_dir, pic_name), "wb") as f:
                f.write(await resp.read())

    # 保存文章
    def save_doc(self, repo_name, title, body):
        # 将不能作为文件名的字符进行编码
        def check_safe_path(path: str):
            for char in r'/\<>?:"|*':
                path = path.replace(char, parse.quote_plus(char))
            return path

        repo_name = check_safe_path(repo_name)
        title = check_safe_path(title)
        save_path = f"{self.yq_config.doc_path}/{repo_name}"
        if not os.path.exists(save_path):
            os.makedirs(save_path)
        save_file = f"{save_path}/{title}.md"
        with open(save_file, "w", encoding="utf-8") as f:
            f.write(body)
